package com.alibam.core.wechat.pay;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.KeyStore;
import java.util.Date;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;
import javax.servlet.ServletInputStream;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;

import com.alibam.core.wechat.common.Config;
import com.alibam.core.wechat.exception.WeChatException;
import com.alibam.core.wechat.jsapi.JsApiUtils;
import com.alibam.core.wechat.util.HttpUtils;
import com.alibam.core.wechat.util.JaxbParser;
import com.alibam.core.wechat.util.PayUtils;

/**
 * 微信支付操作
 * 
 * @author Liuyang at gmail.com
 */
public class PayManager {
	private static Logger logger = Logger.getLogger(PayManager.class);

	// 微信支付统一接口(POST)
	private final static String PAY_UNIFIEDORDER_POST_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	
	// 企业付款接口(POST)
	private final static String PAY_ENTERPRISE_POST_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";

	// p12正式key
	private final static String P12_KEY = "PKCS12";
	
	public static final String CHARSET = "UTF-8";

	private static final String SIGN_TYPE = "MD5";

	public static final String SUCCESS = "SUCCESS";

	public static final String FAIL = "FAIL";

	public PayManager() {
	}

	/**
	 * 生成微信支付-预支付Id
	 * 
	 * @param pay
	 * @return prepayId
	 * @throws WeChatException
	 */
	public String prepayId(PayRequest pay) throws WeChatException {
		logger.info("微信支付， 请求PrepayId.");
		// 处理支付请求，加入必要参数
		pay.setAppid(Config.instance().getAppid());
		pay.setMch_id(Config.instance().getPayMchId());
		pay.setNotify_url(Config.instance().getPayNotifyUrl());
		pay.setSign(PayUtils.createSign(PayUtils.generateObjKVMap(pay), CHARSET, Config.instance().getPayApiKey()));

		JaxbParser reqParser = new JaxbParser(PayBackResult.class);
		reqParser.setCdataNode(pay.getCDataNode());
		String reqStr = reqParser.toXML(pay, false);

		// 发起请求
		String rspStr = HttpUtils.post(PAY_UNIFIEDORDER_POST_URL, reqStr);
		PayResponse rsp = (PayResponse) new JaxbParser(PayResponse.class).toObj(rspStr);

		System.out.println("reqStr :" + reqStr);
		System.out.println("rspStr :" + rspStr);
		// 验证请求并返回结果
		if ("SUCCESS".equals(rsp.getReturn_code().toUpperCase())
				&& "SUCCESS".equals(rsp.getResult_code().toUpperCase())) {
			return rsp.getPrepay_id();
		} else
			throw new WeChatException("获取PrepayId失败." + rsp.getErr_code() + ", " + rsp.getErr_code_des());
	}

	/**
	 * 生成JSAPI支付页面参数。
	 * 
	 * @param prepayId
	 * @return
	 */
	public Map<String, String> generatePayParams(String prepayId) {
		SortedMap<String, String> params = new TreeMap<String, String>();
		params.put("appId", Config.instance().getAppid());
		params.put("timeStamp", Long.toString(new Date().getTime()));
		params.put("nonceStr", PayUtils.createNoncestr());
		params.put("package", "prepay_id=" + prepayId);
		params.put("signType", SIGN_TYPE);
		String paySign = PayUtils.createSign(params, CHARSET, Config.instance().getPayApiKey());
		params.put("paySign", paySign);
		params.put("packageValue", "prepay_id=" + prepayId);
		return params;
	}

	/**
	 * 生成微信支付回调请求, 返回给微信服务端
	 * 
	 * @param is
	 *            ： request.getInputStream()
	 * @return
	 * @throws WeChatException
	 */
	public PayBackResult payBackResult(InputStream is) throws WeChatException {
		JaxbParser jaxbParser = new JaxbParser(PayBackResult.class);
		PayBackResult result = (PayBackResult) jaxbParser.toObj(is);
		if (checkSignature(result))
			return result;
		else
			throw new WeChatException("支付回调失败[验证签名失败]. " + result.getSign());
	}

	/**
	 * 生成微信支付回调请求, 返回给微信服务端
	 * 
	 * @param payBackResutStr
	 *            响应xml
	 * @return
	 * @throws WeChatException
	 */
	public PayBackResult payBackResult(String payBackResutStr) throws WeChatException {
		return this.payBackResult(new ByteArrayInputStream(payBackResutStr.getBytes()));
	}

	public EnterprisePayResponse enterprisePayBackResult(String payBackResutStr) throws WeChatException {
		return this.EnterprisePayResponse(new ByteArrayInputStream(payBackResutStr.getBytes()));
	}

	private EnterprisePayResponse EnterprisePayResponse(ByteArrayInputStream is) {
		JaxbParser jaxbParser = new JaxbParser(EnterprisePayResponse.class);
		EnterprisePayResponse result = (EnterprisePayResponse) jaxbParser.toObj(is);
		return result;
	}

	/**
	 * 对支付回调结果接茬签名
	 * 
	 * @param result
	 * @return
	 */
	public boolean checkSignature(PayBackResult result) {
		return result.getSign().equals(
				PayUtils.createSign(PayUtils.generateObjKVMap(result), CHARSET, Config.instance().getPayApiKey()));
	}

	public String successPayback() {
		return generatePaybackRsp(SUCCESS, "OK");
	}

	public String failPaybackResponse(String errorMsg) {
		return generatePaybackRsp(FAIL, "SOME ERROR");
	}

	private String generatePaybackRsp(String result, String msg) {
		PayBackResponse rsp = new PayBackResponse(result, msg);
		JaxbParser paybackRspParser = new JaxbParser(PayBackResponse.class);
		paybackRspParser.setCdataNode(rsp.getCDataNode());
		return paybackRspParser.toXML(rsp);
	}

	public QrcodePayRequest qrPayRequest(ServletInputStream inputStream) throws WeChatException {
		JaxbParser jaxbParser = new JaxbParser(QrcodePayRequest.class);
		QrcodePayRequest result = (QrcodePayRequest) jaxbParser.toObj(inputStream);
		if (result.getSign().equals(
				PayUtils.createSign(PayUtils.generateObjKVMap(result), CHARSET, Config.instance().getPayApiKey())))
			return result;
		else
			throw new WeChatException("扫码回调失败[验证签名失败]. " + result.getSign());
	}

	public String generateQrPayFailRsp(String string) {
		return generateQrPayFailRsp(FAIL, "SOME ERROR");
	}

	private String generateQrPayFailRsp(String result, String msg) {
		QrcodePayResponse rsp = new QrcodePayResponse(result, msg);
		JaxbParser paybackRspParser = new JaxbParser(QrcodePayResponse.class);
		paybackRspParser.setCdataNode(rsp.getCDataNode());
		return paybackRspParser.toXML(rsp);
	}

	private String generateQrPayBackRsp(String appid, String mch_id, String nonce_str, String prepay_id, String sign) {
		QrcodePayResponse rsp = new QrcodePayResponse(SUCCESS, null, appid, mch_id, nonce_str, prepay_id, SUCCESS, null,
				sign);
		JaxbParser paybackRspParser = new JaxbParser(QrcodePayResponse.class);
		paybackRspParser.setCdataNode(rsp.getCDataNode());
		return paybackRspParser.toXML(rsp);
	}

	public EnterprisePayRequest enterprisePayRequest(ServletInputStream inputStream) throws WeChatException {
		JaxbParser jaxbParser = new JaxbParser(QrcodePayRequest.class);
		EnterprisePayRequest result = (EnterprisePayRequest) jaxbParser.toObj(inputStream);
		if (result.getSign().equals(
				PayUtils.createSign(PayUtils.generateObjKVMap(result), CHARSET, Config.instance().getPayApiKey())))
			return result;
		else
			throw new WeChatException("企业支付回调失败[验证签名失败]. " + result.getSign());
	}

	// public static void main(String[] args) throws WeChatException {
	// PayRequest req = new PayRequest("oC3vsw_qikUi5Hqf9Ad6IURgePlo",
	// "20150111-12345", "商品信息", "100", "10.10.1.143");
	// PayManager payManager = new PayManager();
	//
	// // Test prepayId
	// String prepayId = payManager.prepayId(req);
	//
	// // Test generate Page params.
	// Map<String, String> payParams = payManager.generatePayParams(prepayId);
	// for (String key : payParams.keySet()) {
	// System.out.println("k: " + key + " , v: " + payParams.get(key));
	// }
	//
	// // Test Payback Result.
	// String payBackResutStr =
	// "<xml><appid><![CDATA[wx2421b1c4370ec43b]]></appid><attach><![CDATA[支付测试]]></attach><bank_type><![CDATA[CFT]]></bank_type><fee_type><![CDATA[CNY]]></fee_type><is_subscribe><![CDATA[Y]]></is_subscribe><mch_id><![CDATA[10000100]]></mch_id><nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str><openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid><out_trade_no><![CDATA[1409811653]]></out_trade_no><result_code><![CDATA[SUCCESS]]></result_code><return_code><![CDATA[SUCCESS]]></return_code><sign><![CDATA[2975F338CEF0510D9144D3549E456A8D]]></sign><time_end><![CDATA[20140903131540]]></time_end><total_fee>1</total_fee><trade_type><![CDATA[JSAPI]]></trade_type><transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id></xml>";
	// PayBackResult payResult = payManager.payBackResult(payBackResutStr);
	// System.out.println("3:checkSignature:" +
	// payManager.checkSignature(payResult));
	// System.out.println("3:return_code :" + payResult.getReturn_code());
	// System.out.println("3:result_code :" + payResult.getResult_code());
	// System.out.println("3:is_subscribe :" + payResult.getIs_subscribe());
	// System.out.println("3:transaction_id :" + payResult.getTransaction_id());
	//
	// // Test Payback Response
	// PayBackResponse rspSuccess = new PayBackResponse(SUCCESS, "OK");
	// PayBackResponse rspFail = new PayBackResponse(FAIL, "SOME ERROR");
	// JaxbParser paybackRspParser = new JaxbParser(PayBackResponse.class);
	// paybackRspParser.setCdataNode(rspSuccess.getCDataNode());
	// System.out.println(paybackRspParser.toXML(rspSuccess));
	// System.out.println(paybackRspParser.toXML(rspFail));
	// }

	/**
	 * 
	 * 扫码支付 模式一 生成支付二维码连接
	 * 
	 * @param productId
	 *            商品id, 普通商品 primary:商品id; 活动商品 activity:actprd_id
	 * 
	 */
	public String createQrcodePay(String productId) {
		String time_stamp = JsApiUtils.create_timestamp();
		String nonce_str = PayUtils.createNoncestr();
		String appid = Config.instance().getAppid();
		String mch_id = Config.instance().getPayMchId();

		TreeMap<String, String> map = new TreeMap<String, String>();
		map.put("appid", appid);
		map.put("mch_id", mch_id);
		map.put("nonce_str", nonce_str);
		map.put("product_id", productId);
		map.put("time_stamp", time_stamp);
		logger.info("appid: " + appid + "  mch_id: " + mch_id + "  product_id: " + productId + "  time_stamp: "
				+ time_stamp + "  nonce_str: " + nonce_str + "    ++++++++++++++++++++++");
		String sign = PayUtils.createSign(map, PayManager.CHARSET, Config.instance().getPayApiKey());
		logger.info("sign: " + sign + "    +++++++++++++++++++++");
		return String.format(
				"weixin://wxpay/bizpayurl?sign=%s&appid=%s&mch_id=%s&product_id=%s&time_stamp=%s&nonce_str=%s", sign,
				appid, mch_id, productId, time_stamp, nonce_str);
	}

	/**
	 * 
	 * 扫码支付模式一 生成扫码支付签名
	 * 
	 * @param prepay_id
	 * @param nonce_str
	 * 
	 */
	private String createQrcodePayResponseSign(String prepay_id, String nonce_str) {
		String appid = Config.instance().getAppid();
		String mch_id = Config.instance().getPayMchId();
		String key = Config.instance().getPayApiKey();

		TreeMap<String, String> map = new TreeMap<String, String>();
		map.put("appid", appid);
		map.put("mch_id", mch_id);
		map.put("nonce_str", nonce_str);
		map.put("prepay_id", prepay_id);
		map.put("result_code", "SUCCESS");
		map.put("return_code", "SUCCESS");
		logger.info(
				"appid:" + appid + "   mch_id:" + mch_id + "   nonce_str:" + nonce_str + "  prepay_id:" + prepay_id);
		String sign = PayUtils.createSign(map, PayManager.CHARSET, key);

		logger.info("paykey: " + key);
		logger.info("sign:" + sign);
		return sign;

	}

	/**
	 * 扫码支付模式一 对扫码回调结果检查签名
	 * 
	 * @param result
	 */
	public boolean checkQrcodePaySignature(QrcodePayRequest result) {
		return result.getSign().equals(
				PayUtils.createSign(PayUtils.generateObjKVMap(result), CHARSET, Config.instance().getPayApiKey()));
	}

	/**
	 * 扫码支付模式一: 生成请求返回信息
	 * 
	 * @param openId
	 *            用户appid
	 * @param appId
	 *            公众账号id
	 * @param orderId
	 *            订单编号
	 * @param mch_id
	 *            商户号
	 * @param orderBody
	 *            订单信息
	 * @param money
	 *            金额
	 * @param ip
	 *            ip地址
	 * @param attach
	 * @return
	 * @throws WeChatException
	 */
	public String createQrcodeMode1Rsp(String openId, String appId, String orderId, String mch_id, String orderBody,
			String money, String ip, String attach) throws WeChatException {
		PayRequest payReq = new PayRequest(openId, orderId, orderBody, money, ip);
		// 支付类型
		payReq.setTrade_type("NATIVE");
		payReq.setAttach(attach);
		String prepayId = prepayId(payReq);
		String nonce_str = PayUtils.createNoncestr(32);
		String sign = createQrcodePayResponseSign(prepayId, nonce_str);
		String rspStr = generateQrPayBackRsp(appId, mch_id, nonce_str, prepayId, sign);
		return rspStr;
	}

	/**
	 * 扫码支付模式二: 生成二维码
	 * 
	 * @param openId
	 *            用户appid
	 * @param orderId
	 *            订单编号
	 * @param orderBody
	 *            订单信息
	 * @param money
	 *            金额
	 * @param ip
	 *            ip地址
	 * @param response
	 * @return
	 * @throws Exception
	 */
	public String createQrcodeMode2(String openId, String orderId, String orderBody, String money, String ip)
			throws Exception {
		PayRequest payReq = new PayRequest(openId, orderId, orderBody, money, ip);
		// 支付类型
		payReq.setTrade_type("NATIVE");
		String attach = "attach";
		payReq.setAttach(attach);
		String code_url = getCodeUrl(payReq);
		logger.info("code_url: " + code_url + "     ++++++++++++++++");
		// response.setContentType("image/jpeg");
		// QRCodeUtil.encode(code_url, response.getOutputStream());
		return code_url;
	}

	/**
	 * 
	 * 扫码支付模式二: 获得code_url
	 * 
	 * @param pay
	 * @return code_url
	 */
	private String getCodeUrl(PayRequest pay) throws WeChatException {
		logger.info("微信支付， 请求Code_url.");
		// 处理支付请求，加入必要参数
		pay.setAppid(Config.instance().getAppid());
		pay.setMch_id(Config.instance().getPayMchId());
		pay.setNotify_url(Config.instance().getPayNotifyUrl());
		pay.setSign(PayUtils.createSign(PayUtils.generateObjKVMap(pay), CHARSET, Config.instance().getPayApiKey()));

		JaxbParser reqParser = new JaxbParser(PayBackResult.class);
		reqParser.setCdataNode(pay.getCDataNode());
		String reqStr = reqParser.toXML(pay, false);

		// 发起请求
		String rspStr = HttpUtils.post(PAY_UNIFIEDORDER_POST_URL, reqStr);
		PayResponse rsp = (PayResponse) new JaxbParser(PayResponse.class).toObj(rspStr);

		System.out.println("reqStr :" + reqStr);
		System.out.println("rspStr :" + rspStr);
		// 验证请求并返回结果
		if ("SUCCESS".equals(rsp.getReturn_code().toUpperCase())
				&& "SUCCESS".equals(rsp.getResult_code().toUpperCase())) {
			return rsp.getCode_url();
		} else
			throw new WeChatException("获取code_url失败." + rsp.getErr_code()
					+ ", " + rsp.getErr_code_des());
		
	} 
	
	/**
	 * 企业付款接口
	 * 调用此接口需要在wechat4j.properties中配置p12证书的绝对路径
	 * 举例：wechat.certificatepath=/example/apiclient_cert.p12
	 * 
	 */
    public void enterprisePay(EnterprisePayRequest request) throws Exception {
    	KeyStore keyStore  = KeyStore.getInstance(P12_KEY);
    	String certificateDir = Config.instance().getCertificatePath();
    	String payMchId = Config.instance().getPayMchId();
        FileInputStream instream = new FileInputStream(new File(certificateDir));
        try {
            keyStore.load(instream, payMchId.toCharArray());
        } finally {
            instream.close();
        }

        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadKeyMaterial(keyStore, payMchId.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[] { "TLSv1" },
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        try {

        	HttpPost httpPost = new HttpPost(PAY_ENTERPRISE_POST_URL);
        	JaxbParser parser = new JaxbParser(EnterprisePayRequest.class); 
        	String str = parser.toXML(request);
        	logger.info("str xml : " + str + "      ++++++++++++++++");
        	httpPost.setEntity(new StringEntity(str));
            CloseableHttpResponse response = httpclient.execute(httpPost);
            try {
                HttpEntity entity = response.getEntity();

                System.out.println("----------------------------------------");
                System.out.println(response.getStatusLine());
                if (entity != null) {
                    System.out.println("Response content length: " + entity.getContentLength());
                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
                    String text;
                    String result = "";
                    while ((text = bufferedReader.readLine()) != null) {
                    	result += text;
                    	logger.info("text: " + text + "    ++++++++++++++++++");
                    }
                    logger.info("result: " + result + "    +++++++++++++++++++");
                }
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
}
